package com.example.demo.game;

import com.example.demo.model.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;

/**
 * @program: gobang
 * @author: JCM
 * @create: 2023-03-10 21:10
 * @description: 这个类负责完成匹配工作
 **/
@Component
public class Matcher {

    private Queue<User> normalQueue = new LinkedList<>();
    private Queue<User> highQueue = new LinkedList<>();
    private Queue<User> veryHighQueue = new LinkedList<>();

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Autowired
    private RoomManager roomManager;

    private ObjectMapper objectMapper = new ObjectMapper();

    // 操作匹配队列的方法
    // 把玩家放到匹配队列中
    public void add(User user) {
        if (user.getScore() < 2000) {
            synchronized (normalQueue) {
                normalQueue.offer(user);
                normalQueue.notify();
            }

            System.out.println("把玩家："+user.getUsername()+"加入到 normalQueue 中");
        }else if (user.getScore() >= 2000 && user.getScore() < 3000) {
            synchronized (highQueue) {
                highQueue.offer(user);
                highQueue.notify();
            }
            System.out.println("把玩家："+user.getUsername()+"加入到 highQueue 中");
        }else {
            synchronized (veryHighQueue) {
                veryHighQueue.offer(user);
                veryHighQueue.notify();
            }
            System.out.println("把玩家："+user.getUsername()+"加入到 veryHighQueue 中");

        }
    }

    // 当玩家停止匹配的时候，需要把玩家从匹配队列中删除
    public void remove(User user) {
        if(user.getScore() < 2000) {
            synchronized (normalQueue) {
                normalQueue.remove(user);

            }
            System.out.println("把玩家"+user.getUsername()+"从 normalQueue 中移除了");
        }else if(user.getScore() >= 2000 && user.getScore() < 3000) {
            synchronized (highQueue) {
                highQueue.remove(user);

            }
            System.out.println("把玩家"+user.getUsername()+"从 highQueue 中移除了");
        }else {
            synchronized (veryHighQueue) {
                veryHighQueue.remove(user);

            }
            System.out.println("把玩家"+user.getUsername()+"从 veryHighQueue 中移除了");
        }
    }

    public Matcher() {
        // 创建三个线程，分别针对这三个匹配队列，进行操作
        Thread t1 = new Thread() {
            @Override
            public void run() {
                while (true) {
                    handlerMatch(normalQueue);
                }
            }
        };
        t1.start();

        Thread t2 = new Thread() {
            @Override
            public void run() {
                while (true) {
                    handlerMatch(highQueue);
                }
            }
        };
        t2.start();

        Thread t3 = new Thread() {
            @Override
            public void run() {
                while (true) {
                    handlerMatch(veryHighQueue);
                }
            }
        };
    }

    private void handlerMatch(Queue<User> matchQueue) {
        synchronized (matchQueue) {
            try {
                // 1.检测队列中玩家数是否达到要求
                // 队列的初始状态可能为空
                // 如果往队列中添加一个元素，这个时候，仍然是不能进行匹配操作的
                // 因此在这里使用 while 循环检查更加合适
                while (matchQueue.size() < 2) {
                    matchQueue.wait();
                }
                // 2.尝试从队列中取出两个玩家
                User player1 = matchQueue.poll();
                User player2 = matchQueue.poll();
                System.out.println("匹配到两个玩家："+ player1.getUsername()+"，"+player2.getUsername());
                // 3.获取到玩家的 WebSocket 的会话
                // 获取到会话的目的是为了告诉玩家，你已经匹配到了对手
                WebSocketSession session1 = onlineUserManager.getFromGameHall(player1.getUserId());
                WebSocketSession session2 = onlineUserManager.getFromGameHall(player2.getUserId());
                // 前面已经判定过用户的在线状态，在这里在进行一次判定，可以减少出错几率
                if (session1 == null) {
                    // 如果玩家1 现在不在线了，就把玩家2 重新添加到匹配队列
                    matchQueue.offer(player2);
                    return;
                }
                if (session2 == null) {
                    // 如果玩家2 不在线了，就把玩家1 重新放回匹配队列
                    matchQueue.offer(player1);
                    return;
                }
                // 能否出现 一个玩家同时进入两场对局情况？？？
                // 理论上不会出现
                // 1）如果玩家下线，就会对玩家移除匹配队列
                // 2）禁止了玩家多开
                // 保险起见，我们在这里再进行一次判断，避免前面的代码出现 bug 对现在造成影响
                if (session1 == session2) {
                    // 把其中一个玩家放回到匹配队列
                    matchQueue.offer(player1);
                    return;
                }

                // 4. 把这两个玩家放到一个游戏房间
                Room room = new Room();
                roomManager.add(room,player1.getUserId(),player2.getUserId());


                // 5. 给玩家反馈信息：你匹配到对手了
                // 通过 websocket 返回一个 message 为 ‘matchSuccess’ 的响应
                // 此处需要给两个玩家都返回”匹配成功“的信息，因此需要返回两次
                MatchResponse response1 = new MatchResponse();
                response1.setOk(true);
                response1.setMessage("matchSuccess");
                String json1 = objectMapper.writeValueAsString(response1);
                session1.sendMessage(new TextMessage(json1));

                MatchResponse  response2 = new MatchResponse();
                response2.setOk(true);
                response2.setMessage("matchSuccess");
                String json2 = objectMapper.writeValueAsString(response2);
                session2.sendMessage(new TextMessage(json2));

            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
